﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace NavmeshPathfinding
{
    class Shape
    {
        GraphicsDevice graphics;
        VertexPositionColor[] vertices;
        int[] indexList;

        protected void loadVerticesandIndices(VertexPositionColor[] vertices)
        {   
            if (vertices.Length < 3)
                throw new Exception("Shape: must have at least 3 vertices");

            this.vertices = vertices;

            indexList = new int[(vertices.Length - 2) * 3];
            for (int i = 0, j = 1; i < indexList.Length && j < vertices.Length - 1; j++)
            {
                indexList[i++] = 0;
                indexList[i++] = j;
                indexList[i++] = j + 1;
            }
        }

        protected void loadVerticesandIndices(Vector3[] vertices, Color color)
        {
            if (vertices.Length < 3)
                throw new Exception("Shape: must have at least 3 vertices");

            VertexPositionColor[] colorVertices = new VertexPositionColor[vertices.Length];
            for (int i = 0; i < vertices.Length; i++)
                colorVertices[i] = new VertexPositionColor(vertices[i], color);
                       
            this.vertices = colorVertices;

            indexList = new int[(vertices.Length - 2) * 3];
            for (int i = 0, j = 1; i < indexList.Length && j < vertices.Length - 1; j++)
            {
                indexList[i++] = 0;
                indexList[i++] = j;
                indexList[i++] = j + 1;
            }

        }

        public Shape(GraphicsDevice graphics)
        {
            this.graphics = graphics;
        }

        public Shape(GraphicsDevice graphics, VertexPositionColor[] vertices)
        {
            this.graphics = graphics;
            loadVerticesandIndices(vertices);
        }

        public Shape(GraphicsDevice graphics, Vector3[] vertices, Color color)
        {
            this.graphics = graphics;
            loadVerticesandIndices(vertices,color);
        }

        public virtual void draw()
        {
            graphics.DrawUserIndexedPrimitives<VertexPositionColor>(PrimitiveType.TriangleList, vertices, 0, vertices.Length, indexList, 0, indexList.Length / 3);
        }
    }

    class Triangle : Shape
    {
        public Triangle(GraphicsDevice graphics, Vector3 a, Vector3 b, Vector3 c, Color color)
            : base(graphics)
        {
            loadVerticesandIndices(new Vector3[] { a, b, c }, color);
        }
    }

    class Rectangle : Shape
    {
        public Rectangle(GraphicsDevice graphics, VertexPositionColor[] vertices)
            : base(graphics, vertices)
        {
            if (vertices.Length != 4)
                throw new Exception("Rectangle is not a rectangle");
        }

        public Rectangle(GraphicsDevice graphics, Vector3 a, Vector3 b, Vector3 c, Vector3 d, Color color)
            : base(graphics)
        {
            loadVerticesandIndices(new Vector3[] { a, b, c, d }, color);
        }
    }

    class Circle : Shape
    {
        public Circle(GraphicsDevice graphics, Vector3 center, Vector3 normal, Vector3 startEdge, float radius, float angle, Color color)
            : base(graphics)
        {
            bool isFullCircle = Mathf.isEqual(angle,2*Mathf.PI);
            
            int fanNum = (int) (angle / Config.Shape.DELTA_RADIAN);
            fanNum = (fanNum == 0) ? 1 : fanNum;
            float fanAngle = angle / fanNum;

            int vertexNum = 2 + fanNum;
            vertexNum = (isFullCircle) ? vertexNum - 2 : vertexNum;

            Vector3[] vertexList = new Vector3[vertexNum];
            if(!isFullCircle) vertexList[0] = center;

            startEdge.Normalize();
            Matrix rotation = Matrix.CreateFromAxisAngle(normal,- fanAngle);
            for (int i = isFullCircle?0:1; i < vertexList.Length; i++, startEdge = Vector3.Transform(startEdge, rotation))
                vertexList[i] = center + startEdge * radius;

            loadVerticesandIndices(vertexList, color);
        }
    }

    class Line : Shape
    {
        List<Shape> shapeList;

        public Line(GraphicsDevice graphics, Vector3 a, Vector3 b, Vector3 normal, float lineWidth, Color color) : base(graphics)
        {
            shapeList = new List<Shape>();

            Vector3 horizontal = Vector3.Cross(b - a, normal);
            horizontal.Normalize();
            shapeList.Add(new Circle(graphics, a, normal, horizontal, lineWidth, Mathf.PI, color));
            shapeList.Add(new Circle(graphics, b, normal, -1 * horizontal, lineWidth, Mathf.PI, color));

            Vector3 rectV1 = a - horizontal * lineWidth;
            Vector3 rectV2 = b - horizontal * lineWidth;
            Vector3 rectV3 = b + horizontal * lineWidth;
            Vector3 rectV4 = a + horizontal * lineWidth;
            shapeList.Add(new Rectangle(graphics, rectV1, rectV2, rectV3, rectV4, color));
        }

        public override void draw()
        {
            foreach (Shape shape in shapeList)
                shape.draw();
        }
    }

    class Arrow : Shape
    {
        List<Shape> shapeList;

        public Arrow(GraphicsDevice graphics, Vector3 a, Vector3 b, Vector3 normal, float lineWidth, float arrowWidth, float arrowLength, Color color)
            : base(graphics)
        {
            shapeList = new List<Shape>();

            Vector3 c = b;
            Vector3 v = b - a;
            v.Normalize();
            b = b - v * arrowLength;

            Vector3 horizontal = Vector3.Cross(b - a, normal);
            horizontal.Normalize();
            shapeList.Add(new Circle(graphics, a, normal, horizontal, lineWidth, Mathf.PI, color));
            shapeList.Add(new Circle(graphics, b, normal, -1 * horizontal, lineWidth, Mathf.PI, color));

            Vector3 rectV1 = a - horizontal * lineWidth;
            Vector3 rectV2 = b - horizontal * lineWidth;
            Vector3 rectV3 = b + horizontal * lineWidth;
            Vector3 rectV4 = a + horizontal * lineWidth;
            shapeList.Add(new Rectangle(graphics, rectV1, rectV2, rectV3, rectV4, color));

            shapeList.Add(new Triangle(graphics, b - horizontal * arrowWidth, c, b + horizontal * arrowWidth, color));
        }

        public override void draw()
        {
            foreach (Shape shape in shapeList)
                shape.draw();
        }
    }

    class LineStrip : Shape
    {
        List<Shape> shapeList;

        public LineStrip(GraphicsDevice graphics, Vector3[] vertexList, Vector3[] normalList, float lineWidth, Color color)
            : base(graphics)
        {
            shapeList = new List<Shape>();

            for (int i = 0; i < vertexList.Length - 1; i++)
                shapeList.Add(new Line(graphics, vertexList[i], vertexList[i + 1], normalList[i], lineWidth, color));
        }

        public override void draw()
        {
            foreach (Shape shape in shapeList)
                shape.draw();
        }
    }
}
